﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace RDNS.Kernel
{
    public static class StreamExtension
    {
        public static IAsyncResult BeginRead(this Stream stream, byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
        {
            var asyncResult = new ExtendAsyncResult(asyncState);
            ThreadPool.QueueUserWorkItem(Read, new AsyncUserToken(stream, asyncState)
            {
                buffer = buffer,
                Offset = offset,
                Lenth = count,
                Callback = asyncCallback,
                AsyncResult = asyncResult,
            });
            return asyncResult;
        }

        public static int EndRead(this Stream stream, IAsyncResult ar)
        {
            var asyncResult = ar as ExtendAsyncResult;
            asyncResult.AsyncWaitHandle.WaitOne();
            return (asyncResult.AsyncState as AsyncUserToken).BytesTransferred;
        }

        private static void Read(object state)
        {
            var token = state as AsyncUserToken;
            token.BytesTransferred = token.Stream.Read(token.buffer, token.Offset, token.Lenth);
            token.AsyncResult.IsCompleted = true;
            token.Callback(token.AsyncResult);
            (token.AsyncResult.AsyncWaitHandle as AutoResetEvent).Set();
        }

        public static IAsyncResult BeginWrite(this Stream stream, byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState)
        {
            var asyncResult = new ExtendAsyncResult(asyncState);
            ThreadPool.QueueUserWorkItem(Write, new AsyncUserToken(stream, asyncState)
            {
                buffer = buffer,
                Offset = offset,
                Lenth = count,
                Callback = asyncCallback,
                AsyncResult = asyncResult,
            });
            return asyncResult;
        }

        public static void EndWrite(this Stream stream, IAsyncResult ar)
        {
            var asyncResult = ar as ExtendAsyncResult;
            asyncResult.AsyncWaitHandle.WaitOne();
        }

        private static void Write(object state)
        {
            var token = state as AsyncUserToken;
            token.Stream.Write(token.buffer, token.Offset, token.Lenth);
            token.AsyncResult.IsCompleted = true;
            token.Callback(token.AsyncResult);
            (token.AsyncResult.AsyncWaitHandle as AutoResetEvent).Set();
        }
    }

    public class AsyncUserToken
    {
        public byte[] buffer { get; set; }
        public int Offset { get; set; }
        public int Lenth { get; set; }
        public AsyncUserToken(Stream stream, object asyncState)
        {
            Stream = stream;
            AsyncState = asyncState;
        }

        public object AsyncState { get; set; }
        public Stream Stream { get; set; }
        public AsyncCallback Callback { get; set; }
        public int BytesTransferred { get; internal set; }
        public ExtendAsyncResult AsyncResult { get; internal set; }
    }
    public class ExtendAsyncResult : IAsyncResult
    {
        public ExtendAsyncResult(object asyncState)
        {
            this.AsyncState = asyncState;
            this.AsyncWaitHandle = new AutoResetEvent(false);
        }

        public object AsyncState { get; private set; }

        public WaitHandle AsyncWaitHandle { get; private set; }

        public bool CompletedSynchronously { get; internal set; }

        public bool IsCompleted { get; internal set; }
    }
}
